/*
 * Copyright (C) 2010 Cyril Mottier (http://www.cyrilmottier.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.xspace.android.xafview.imgview.image;

import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import android.text.TextUtils;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;

import com.xspace.android.xafview.imgview.image.AsyncImageView.LoadType;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * An ImageLoader asynchronously loads image from a given url. Client may be
 * notified from the current image loading state using the
 * {@link ImageLoaderCallback}.
 * <p>
 * <em><strong>Note: </strong>You normally don't need to use the {@link ImageLoader}
 * class directly in your application. You'll generally prefer using an
 * {@link ImageRequest} that takes care of the entire loading process.</em>
 * </p>
 * 
 * @author Cyril Mottier
 */
public class ImageLoader {

	private static final String LOG_TAG = ImageLoader.class.getSimpleName();

	/**
	 * @author Cyril Mottier
	 */
	public static interface ImageLoaderCallback {

		void onImageLoadingStarted(ImageLoader loader);

		void onImageLoadingEnded(ImageLoader loader, Bitmap bitmap);

		void onImageLoadingFailed(ImageLoader loader, Throwable exception);
	}

	private static final int ON_START = 0x100;
	private static final int ON_FAIL = 0x101;
	private static final int ON_END = 0x102;

	private static ImageCache sImageCache;
	private static ExecutorService sExecutor;
	private static BitmapFactory.Options sDefaultOptions;
	private static AssetManager sAssetManager;
    public String url;
    public String group_id;
    public int key;
	public ImageLoader(Context context) {
		if (sImageCache == null) {
			sImageCache = GDUtils.getImageCache(context);
		}
		if (sExecutor == null) {
			sExecutor = GDUtils.getExecutor(context);
		}
		if (sDefaultOptions == null) {
			sDefaultOptions = new BitmapFactory.Options();
			sDefaultOptions.inDither = true;
			sDefaultOptions.inScaled = true;
			sDefaultOptions.inDensity = DisplayMetrics.DENSITY_MEDIUM;
			sDefaultOptions.inTargetDensity = context.getResources()
					.getDisplayMetrics().densityDpi;
		}
		sAssetManager = context.getAssets();
	}

	public Future<?> loadImage(String groupid,int key,String url, ImageLoaderCallback callback,BitmapFactory.Options options) {
		this.url=url;
		this.key=key;
		this.group_id=groupid;
		return loadImage(url, callback, null,options);
	}
	public Future<?> loadImage(String url, ImageLoaderCallback callback) {
		this.url=url;
		return loadImage(url, callback, null);
	}
	public Future<?> loadImage(String url, ImageLoaderCallback callback,
			ImageProcessor bitmapProcessor) {
		return loadImage(url, callback, bitmapProcessor, null);
	}

	public Future<?> loadImage(String url, ImageLoaderCallback callback,
			ImageProcessor bitmapProcessor, BitmapFactory.Options options) {
		return sExecutor.submit(new ImageFetcher(url, callback,
				bitmapProcessor, options));
	}

	public Future<?> loadImage(String url, int loadType,
			ImageLoaderCallback callback, ImageProcessor bitmapProcessor,
			BitmapFactory.Options options) {
		return sExecutor.submit(new ImageFetcher(url, loadType, callback,
				bitmapProcessor, options));
	}

	private class ImageFetcher implements Runnable {

		private String mUrl;
		private ImageHandler mHandler;
		private ImageProcessor mBitmapProcessor;
		private BitmapFactory.Options mOptions;
		private int mLoadType;

		public ImageFetcher(String url, ImageLoaderCallback callback,
				ImageProcessor bitmapProcessor, BitmapFactory.Options options) {
			mUrl = url;
			mHandler = new ImageHandler(url, callback);
			mBitmapProcessor = bitmapProcessor;
			mOptions = options;
		}

		public ImageFetcher(String url, int loadType,
				ImageLoaderCallback callback, ImageProcessor bitmapProcessor,
				BitmapFactory.Options options) {
			mUrl = url;
			mLoadType = loadType;
			mHandler = new ImageHandler(url, callback);
			mBitmapProcessor = bitmapProcessor;
			mOptions = options;
		}

		public void run() {

			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

			final Handler h = mHandler;
			Bitmap bitmap = null;
			Throwable throwable = null;

			h.sendMessage(Message.obtain(h, ON_START));

			// Pictures from sdcard read cache
			if (mLoadType == LoadType.URL.ordinal()) {
				//Log.v("imagloader", "00>>>>>>>>>>>>>>>>>");
				bitmap = getFromSdcard(mUrl);
			}
			if (bitmap == null && mLoadType == LoadType.URL.ordinal()) {
				try {

					if (TextUtils.isEmpty(mUrl)) {
						throw new Exception(
								"The given URL cannot be null or empty");
					}
					//Log.v("imagloader", "11>>>>>>>>>>>>>>>>>");
					InputStream inputStream = null;

					if (mUrl.startsWith("file:///android_asset/")) {
						inputStream = sAssetManager.open(mUrl.replaceFirst(
								"file:///android_asset/", ""));
					} else {
						inputStream = new URL(mUrl).openStream();
					}

					// 图片缓存本地
					bitmap = savePicToSdcard(inputStream, mUrl);
					if (bitmap == null) {
						//Log.v("imagloader", "12>>>>>>>>>>>>>>>>>"+url+" mOptions null:"+(mOptions==null));
						// TODO Cyril: Use a AndroidHttpClient?
						inputStream.reset();
						bitmap = BitmapFactory
								.decodeStream(inputStream, null,
										(mOptions == null) ? sDefaultOptions
												: mOptions);
					}

					if (mBitmapProcessor != null && bitmap != null) {
						//Log.v("imagloader", "13>>>>>>>>>>>>>>>>>"+url);
						final Bitmap processedBitmap = mBitmapProcessor
								.processImage(bitmap);
						if (processedBitmap != null) {
							bitmap = processedBitmap;
						}
					}

				} catch (Exception e) {
					// An error occured while retrieving the image
					if (Config.GD_ERROR_LOGS_ENABLED) {
						Log.e(LOG_TAG, "Error while fetching image", e);
					}
					throwable = e;
				}
			} else if (bitmap == null && mLoadType == LoadType.BINARY.ordinal()) {
				//Log.v("imagloader", "22>>>>>>>>>>>>>>>>>");
				bitmap = binartToBitmap(mUrl);
			}

			if (bitmap == null) {
				if (throwable == null) {
					// Skia returned a null bitmap ... that's usually because
					// the given url wasn't pointing to a valid image
					throwable = new Exception("Skia image decoding failed");
				}
				h.sendMessage(Message.obtain(h, ON_FAIL, throwable));
			} else {
				h.sendMessage(Message.obtain(h, ON_END, bitmap));
			}
		}

		public File getCacheFile(String imageUri) {
			File cacheFile = null;
			try {
				if (Environment.getExternalStorageState().equals(
						Environment.MEDIA_MOUNTED)) {
					File sdCardDir = Environment.getExternalStorageDirectory();
					String fileName = getFileName(imageUri);
					File dir = new File(sdCardDir.getCanonicalPath()
							+ "/pic_cache");
					if (!dir.exists()) {
						dir.mkdirs();
					}
					cacheFile = new File(dir, fileName);
					Log.v("imagloader", "exists:" + cacheFile.exists() + ",dir:"
							+ dir + ",file:" + fileName);
				}
			} catch (IOException e) {
				e.printStackTrace();
				Log.v("imagloader","getCacheFileError:" + e.getMessage());
			}

			return cacheFile;
		}

		public String getFileName(String path) {
			int index = path.lastIndexOf("/");
			return path.substring(index + 1);
		}

		public Bitmap binartToBitmap(String binary) {
			Bitmap bitmap = null;
			try {
				byte[] bitmapArray;
				bitmapArray = Base64.decode(binary.getBytes("utf-8"),
						Base64.DEFAULT);

				bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0,
						bitmapArray.length);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return bitmap;
		}

		private Bitmap savePicToSdcard(InputStream is, String imageUri) {
			Bitmap bitmap = null;
			try {
				File cacheFile = getCacheFile(imageUri);
				BufferedOutputStream bos = null;
				bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
				System.out.println("write file to "
						+ cacheFile.getCanonicalPath());

				byte[] buf = new byte[1024];
				int len = 0;
				// 将网络上的图片存储到本地
				while ((len = is.read(buf)) > 0) {
					bos.write(buf, 0, len);
				}

				// is.close();
				bos.close();
				bitmap = BitmapFactory.decodeFile(cacheFile.getAbsolutePath(),(mOptions == null) ? sDefaultOptions
						: mOptions);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return bitmap;
		}

		/**
		 * Pictures from sdcard read cache
		 * 
		 * @return
		 */
		private Bitmap getFromSdcard(String url) {
//			Bitmap bitmap = BitmapFactory.decodeFile(getCacheFile(url)
//					.getAbsolutePath(), (mOptions == null) ? sDefaultOptions
//							: mOptions);
			
			if(mOptions!=null){
				mOptions.inJustDecodeBounds=true;
			}
			String catheFilepath=getCacheFile(url).getAbsolutePath();
			Bitmap bitmap =BitmapFactory.decodeFile(catheFilepath, (mOptions == null) ? sDefaultOptions
							: mOptions);
			if(mOptions!=null&&mOptions.inJustDecodeBounds){
				int width=mOptions.outWidth;
				int height=mOptions.outHeight;
				int scale_x=width/100;
				int scale_y=height/100;
				if(scale_x<scale_y){
					scale_x=scale_y;
				}
				if(scale_x==0) scale_x=1;
				//Log.v("imagloader", "getFromSdcard 16>>>>>>>>>>>>>>>>>scale_x:"+scale_x);
				mOptions.inSampleSize=scale_x;
				mOptions.inJustDecodeBounds=false;
				bitmap =BitmapFactory.decodeFile(catheFilepath,(mOptions == null) ? sDefaultOptions
						: mOptions);
			}
			return bitmap;
		}

	}

	private class ImageHandler extends Handler {

		private String mUrl;
		private ImageLoaderCallback mCallback;

		private ImageHandler(String url, ImageLoaderCallback callback) {
			mUrl = url;
			mCallback = callback;
		}

		@Override
		public void handleMessage(Message msg) {

			switch (msg.what) {

			case ON_START:
				if (mCallback != null) {
					mCallback.onImageLoadingStarted(ImageLoader.this);
				}
				break;

			case ON_FAIL:
				if (mCallback != null) {
					mCallback.onImageLoadingFailed(ImageLoader.this,
							(Throwable) msg.obj);
				}
				break;

			case ON_END:

				final Bitmap bitmap = (Bitmap) msg.obj;
				sImageCache.put(mUrl, bitmap);

				if (mCallback != null) {
					mCallback.onImageLoadingEnded(ImageLoader.this, bitmap);
				}
				break;

			default:
				super.handleMessage(msg);
				break;
			}
		};
	}

}
